
/***************************************************************************

Copyright (c) Microsoft Corporation. All rights reserved.
This code is licensed under the Visual Studio SDK license terms.
THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.

***************************************************************************/

using System;
using System.Linq;
using System.Runtime.InteropServices;
using System.CodeDom.Compiler;
using System.CodeDom;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Schema;
using Microsoft.Win32;
using Microsoft.VisualStudio.Shell;
using VSLangProj80;
using EnvDTE;
using System.Collections.Generic;
using Microsoft.VisualStudio.Package;
using NBusiness.ESharp;
using NBusiness.ESharp.Compiler;
using NBusiness.Templates;
using NBusiness.CodeDom;
using NBusiness.CodeDom.Compiler;
using System.Collections;
using NBusiness.CodeDom.Services;

namespace NBusiness.VisualStudio
{
    /// <summary>
    /// This is the generator class. 
    /// When setting the 'Custom Tool' property of a C#, VB, or J# project item to "XmlClassGenerator", 
    /// the GenerateCode function will get called and will return the contents of the generated file 
    /// to the project system
    /// </summary>
    [ComVisible(true)]
    [Guid("52B316AA-1997-4c81-9969-83604C09EEB4")]
    [Serializable]
    public class ESharpCodeGenerator : BaseCodeGeneratorWithSite
    {

#pragma warning disable 0414
        //The name of this generator (use for 'Custom Tool' property of project item)
        internal static string name = "ESharpCodeGenerator";
#pragma warning restore 0414

        /// <summary>
        /// Function that builds the contents of the generated file based on the contents of the input file
        /// </summary>
        /// <param name="inputFileContent">Content of the input file</param>
        /// <returns>Generated file as a byte array</returns>
        protected override byte[] GenerateCode(string inputFileContent)
        {
            CodeDomProvider provider = GetCodeProvider();
            try
            {
                if (this.CodeGeneratorProgress != null)
                {
                    //Report that we are 1/2 done
                    this.CodeGeneratorProgress.Progress(0, 100);
                }

                // Initialize
                ESharpLanguageService languageService = ESharpLanguageService.Current;
                Project project = GetProject();

                // Create provider and services.
                ESharpReflectionService finder = new ESharpReflectionService(project);
                ESharpProvider esharpProvider = new ESharpProvider();

                ESharpFrameworkService frameworkService = new ESharpFrameworkService(finder);
                ESharpBuildErrorService errorService = new ESharpBuildErrorService();
                ESharpCodeDomService codeDomService = new ESharpCodeDomService(provider);

                esharpProvider.SetService(typeof(IFrameworkService), frameworkService);
                esharpProvider.SetService(typeof(IBuildErrorService), errorService);
                esharpProvider.SetService(typeof(ICodeDomService), codeDomService);
                
                // Parse files
                EntityFile[] files = languageService.GetFiles(project);
                EntityParser parser = esharpProvider.GetParser();
                ESharpRoot root = parser.ParseFiles(files) as ESharpRoot;
                                
                if (errorService.Errors.Length > 0)
                {
                    ReportErrors(errorService.Errors);
                }
                else
                {

                    Entity[] entities = root.FindByPath(InputFilePath);

                    List<EntityFile> compiledFiles = new List<EntityFile>();
                    using (EntityCompiler compiler = esharpProvider.GetCompiler())
                    {
                        foreach (Entity entity in entities)
                        {
                            EntityFile[] results = compiler.CompileEntity<CodeTemplateAttribute>(
                                entity,
                                new CodeTemplateParameters { Provider = provider });

                            if (results != null)
                                compiledFiles.AddRange(results);
                        }
                    }

                    ProjectItem item = GetProjectItem();
                    foreach (EntityFile file in compiledFiles)
                    {
                        string path = string.Format("{0}\\{1}.{2}.{3}",
                            Path.GetDirectoryName(this.InputFilePath),
                            Path.GetFileNameWithoutExtension(this.InputFilePath),
                            Path.GetFileNameWithoutExtension(file.FileName).ToLower(),
                            this.GetDefaultExtension());

                        using (FileStream fs = File.Create(path))
                        {
                            using (TextWriter writer = new StreamWriter(fs))
                            {
                                writer.Write(file.Data);
                                writer.Flush();
                            }
                        }
                        item.ProjectItems.AddFromFile(path);                   
                    }

                    foreach (ProjectItem i in item.ProjectItems)
                    {
                        EntityFile file = compiledFiles.Find(e => i.Name.ToLower().Contains(e.FileName.ToLower()));
                        if (file == null)
                            i.Delete();
                    }

                    languageService.UpdateReflectionInformation(provider);
                    project.Save(project.FileName);
                }
            }
            catch (Exception e)
            {
                this.GeneratorError(4, e.Message, 1, 1);
            }
            finally
            {
                if (this.CodeGeneratorProgress != null)
                {
                    //Report that we are done
                    this.CodeGeneratorProgress.Progress(100, 100);
                }
            }

            return null;
        }

        private void ReportErrors(EntityBuildError[] errors)
        {
            foreach (EntityBuildError error in errors)
                this.GeneratorError(((uint)error.Type) + 1, error.Message, (uint)error.Element.Line, (uint)error.Element.Column);
        }
    }
}